#include "CentralCache.h"
#include "PageCache.h"

CentralCache CentralCache::centralCache;

Span *CentralCache::getOneSpan(SpanList &spanList, size_t size)
{
    Span *spanBegin = spanList.begin();
    Span *spanEnd = spanList.end();
    while (spanBegin != spanEnd)
    {
        if (spanBegin->_freeList)
        {
            return spanBegin;
        }
        else
        {
            spanBegin = spanBegin->_next;
        }
    }

    // 需要多少页
    size_t nPage = SizeClass::maxNPage(size);

    // 将桶锁打开，方便其他ThreadCache归还内存资源
    spanList._mtx.unlock();

    // 添加全局(整个PageCache)锁
    PageCache::getInstance().getMtx().lock();
    Span *span = PageCache::getInstance().newSpan(nPage);
    // 当前span设置为在用
    span->_isUse = true;

    PageCache::getInstance().getMtx().unlock();

    char *start = (char *)(span->_page_id << PAGE_SHIFT);
    size_t bytes = span->_n << PAGE_SHIFT;
    char *end = start + bytes;

    // 将大span切分为小块内存
    span->_freeList = start;
    start += size;
    void *tail = span->_freeList;

    // printMem(&tail);
    while (start < end)
    {
        // 打印小块内存地址
        // printMem(&tail);

        nextObj(tail) = start;
        start += size;
        tail = nextObj(tail);
    }
    // printMem(&tail);

    // 添加桶锁，并将非空span挂载到spanList中
    spanList._mtx.lock();
    spanList.pushFront(span);

    // 返回非空的span
    return span;
}

int CentralCache::alloc(void *&start, void *&end, size_t size, size_t batchNum)
{
    size_t index = SizeClass::index(size);

    _freeList[index]._mtx.lock();

    Span *span = getOneSpan(_freeList[index], size);

    assert(span);
    assert(span->_freeList);

    size_t i = 1;
    size_t actualNum = 1;
    start = span->_freeList;
    end = start;
    while (i < batchNum && nextObj(end))
    {
        end = nextObj(end);
        i++;
        actualNum++;
    }

    span->_freeList = nextObj(end);
    nextObj(end) = nullptr;
    span->_use_count += actualNum;

    _freeList[index]._mtx.unlock();

    return actualNum;
}

void CentralCache::dealloc(void *&start, void *&end, size_t size)
{
    // 找到要归还的桶
    size_t index = SizeClass::index(size);
    _freeList[index]._mtx.lock();

    while (start)
    {
        void *next = nextObj(start);

        // 通过地址，找到所属页号的span，并挂在到span的freelist
        PAGE_ID startPageId = (PAGE_ID)start >> PAGE_SHIFT;
        Span *span = PageCache::getInstance().pageIdSpanMap(startPageId);
        nextObj(start) = span->_freeList;
        span->_freeList = start;
        span->_use_count--;

        // 如果当前桶的当前span的所有小内存都还回来了，那么可以将这个span还给pageCache
        if (span->_use_count == 0)
        {
            _freeList[index].erase(span);
            span->_pre = nullptr;
            span->_next = nullptr;
            span->_freeList = nullptr;

            _freeList[index]._mtx.unlock();

            PageCache::getInstance().getMtx().lock();
            PageCache::getInstance().dealloc(span);
            PageCache::getInstance().getMtx().unlock();

            _freeList[index]._mtx.lock();
        }

        start = next;
    }

    _freeList[index]._mtx.unlock();
}